#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <asm/current.h>
#include <linux/delay.h>


static  int major;
struct class *hc_key_class;

static int flag;
static int key_status;

struct gpioirq{
	int gpio;
	int irq;
	struct timer_list key_timer;
	struct tasklet_struct tasklet;
	struct work_struct work;
};

static struct gpioirq *gp_irq;

static DECLARE_WAIT_QUEUE_HEAD(key_waitqueue);

static irqreturn_t key_threaded_func_handler(int irq, void *dev)
{
	int i = 0;
	struct gpioirq *girq = dev;

	printk("key_threaded_func_handler run, gpio = %d\n", girq->gpio);
	printk("the process is\"%s\"(pid %d)\n",current->comm, current->pid);

	while(i < 5){
		printk("%d ",i++);
		msleep(500);
	}
	printk("\n");
	return IRQ_HANDLED;
}

struct fasync_struct *button_fasync;

/* 中断下半部处理函数        	工作队列（运行于内核线程上下文）
   可以被外部中断打断，也会被定时中断打断
   运行期间应用程序可以得到执行，但实时性会降低 */
static void my_work_func(struct work_struct *work)
{
	printk("my_work_func run\n");
}

/* 中断下半部处理函数 tasklet（运行于中断上下文）
   可以被外部中断打断，但不会被定时中断打断
   运行期间应用程序得不到执行 */
static void my_tasklet_func(unsigned long arg)
{
	printk("my_tasklet_func gpio = %d\n", (int)arg);
}

static void key_expire_timeout(unsigned long arg)
{
	int value;
	struct gpioirq *girq = (struct gpioirq *)arg;
	//消除第一次定时到
	static int i=0;
	if(i < 2){
		i++;
		return;
	}
	
	value = gpio_get_value(girq->gpio);
	key_status = (girq->gpio << 8) | value;

	flag = 1;
	if(value == 0)
		printk("key_expire_timeout happend, irq = %d, gpio = %d, key down\n\n", girq->irq, girq->gpio);
	else
		printk("key_expire_timeout happend, irq = %d, gpio = %d, key up\n\n", girq->irq, girq->gpio);

//	wake_up_interruptible(&key_waitqueue);
	kill_fasync(&button_fasync, SIGIO, POLL_IN);
}

static ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
	int err;
//	wait_event_interruptible(key_waitqueue, key_status);
	err = copy_to_user(buf, &key_status, 4);
	key_status = 0;
	flag = 0;
	return 1;
}

static unsigned int key_poll(struct file *fp, struct poll_table_struct *wait)
{
	poll_wait(fp, &key_waitqueue, wait);
	printk("key_poll timeout!\n");

	return flag ? (POLLIN | POLLRDNORM) : 0;
}

static int key_fasync (int fd, struct file *filp, int on)
{
	int retval;
	retval = fasync_helper(fd, filp, on, &button_fasync);
	if (retval < 0)
		return retval;

	return 0;
}

static struct file_operations key_ops = {
	.owner 		=	THIS_MODULE,
	.read 		=   key_read,
	.poll 		=   key_poll,
	.fasync		=   key_fasync,
};

static irqreturn_t key_irq_handler(int irq, void *dev)
{
	struct gpioirq *girq = dev;

	/* 调度tasklet */
	tasklet_schedule(&girq->tasklet);

	/* 调度工作队列执行 */
	schedule_work(&girq->work);

	/* 产生中断时修改定时器计时停止时间 */
	mod_timer(&girq->key_timer, jiffies + HZ/50);

	printk("I first run, gpio = %d\n", girq->gpio);
	
	return IRQ_WAKE_THREAD;	 
}

/*=============================platform_driver==============================*/
/* 匹配内核根据设备树生成的platform_device */
static int my_probe(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	int gpio_cnt,i,err;
	
	/* 获得GPIO个数 */
	gpio_cnt = of_gpio_count(node);

	/* 向内核申请空间，存储gpio号和irq , GFP_KERNEL = 0, 申请到的内存默认清零*/
	gp_irq = kzalloc(gpio_cnt * sizeof(struct gpioirq), GFP_KERNEL);

	for(i = 0; i < gpio_cnt; i++){
		//获取GPIO
		gp_irq[i].gpio = of_get_gpio(node, i);
		
		//GPIO转换成中断号
		gp_irq[i].irq = gpio_to_irq(gp_irq[i].gpio);

		//中断请求
//		err = request_irq(gp_irq[i].irq, key_irq_handler,  IRQF_TRIGGER_FALLING, "hc_key_irq", &gp_irq[i]);

//		init_timer(gp_irq[i].key_timer);
		/* 1.初始化定时器，传入GPIO设备参数 */
		setup_timer(&gp_irq[i].key_timer, key_expire_timeout, (unsigned long)&gp_irq[i]);
		/* 2.设置定时器的超时时间 */
		gp_irq[i].key_timer.expires = 0;
		/* 3.注册内核定时器，将定时器加人到内核动态定时器链表中 */
		add_timer(&gp_irq[i].key_timer);

		/* 中断下半部：tasklet */
		tasklet_init(&gp_irq[i].tasklet, my_tasklet_func, (unsigned int)gp_irq[i].gpio);

		/* 中断下半部：work工作队列 */
		INIT_WORK(&gp_irq[i].work, my_work_func);

		/* 中断下半部：threaded_irq        IRQF_ONESHOT:在上下文中屏蔽对应中断号 */
		err = request_threaded_irq(gp_irq[i].irq, key_irq_handler, key_threaded_func_handler, IRQF_TRIGGER_FALLING, "hc_key_irq", &gp_irq[i]);
				
	}

	printk("my_probe run\n");
	
	return 0;
}

static int my_remove(struct platform_device *pdev)
{
	struct device_node *node = pdev->dev.of_node;
	int gpio_cnt,i;
	
	/* 获得GPIO个数 */
	gpio_cnt = of_gpio_count(node);

	for(i = 0; i < gpio_cnt; i++){
		free_irq(gp_irq[i].irq, &gp_irq[i]);
		del_timer(&gp_irq[i].key_timer);
		tasklet_kill(&gp_irq[i].tasklet);
	}

	printk("my_remove run\n");

	return 0;
}

static struct of_device_id hc_key_match[] = {
	{.compatible = "hc-key"}, 
	{},
};

static struct platform_driver dev_driver = {
	.probe		=	my_probe,	
	.remove		= 	my_remove,
	.driver		= {
		.name	= "my_platform_driver",
		.of_match_table = hc_key_match,
	},
};

/*=============================驱动出入口函数==============================*/
/* 驱动入口函数：insmod xx.ko 时会被调用 */
static int dev_init(void)
{	
	major = register_chrdev(0, "key_driver", &key_ops);

	hc_key_class = class_create(THIS_MODULE, "key_class");
	if(IS_ERR(hc_key_class)){
		printk("hc_key_class create failed\n");
		return 1;
	}

	device_create(hc_key_class, NULL, MKDEV(major, 0), NULL, "key");
	
	platform_driver_register(&dev_driver);

	return 0;
}

/* 驱动出口函数： rmmod xx.ko 时会被调用 */
static void dev_exit(void)
{
	platform_driver_unregister(&dev_driver);
	
	device_destroy(hc_key_class, MKDEV(major, 0));
	
	class_destroy(hc_key_class);
	
	unregister_chrdev(major, "key_driver");
	
	printk("hc_key driver exit\n");
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
